package com.autumn.platform.web.resulthandler.wrapper.impl;

import java.util.HashMap;
import java.util.Map;

import org.springframework.core.MethodParameter;
import org.springframework.core.Ordered;

import com.autumn.platform.core.Utils;
import com.autumn.platform.core.context.response.IResponse;
import com.autumn.platform.core.context.response.IResponseContext;
import com.autumn.platform.core.support.order.impl.Orderable;
import com.autumn.platform.web.annotation.JsonBody;
import com.autumn.platform.web.exceptionresolver.ExceptionStatusResolver;
import com.autumn.platform.web.resulthandler.wrapper.ResultWrapper;

public class MapResultWrapper extends Orderable implements ResultWrapper {

    private String codePropertyName = "code";

    private String messagePropertyName = "message";

    private String valuePropertyName = "data";

    private String statusPropertyName = "success";

    private String validateErrorsPropertyName = "validates";

    private String trackIdPropertyName = "trackId";

    private ThreadLocal<IResponse> response = new ThreadLocal<IResponse>();

    private ThreadLocal<JsonBody> jsonBody = new ThreadLocal<JsonBody>();

    public MapResultWrapper() {
        super(Ordered.LOWEST_PRECEDENCE);
    }

    public IResponse getStatus() {
        return this.response.get();
    }

    public JsonBody getJsonBody() {
        return this.jsonBody.get();
    }

    @Override
    public boolean supports(Object result, MethodParameter resultType) {
        return true;
    }

    @Override
    public Object wrap(Object result, MethodParameter resultType) {
        try {
            JsonBody jsonBody = this.initJsonBody(resultType);
            IResponse response = this.initStatus(result);

            Map<String, Object> wrap = new HashMap<String, Object>();
            this.doWrapCode(wrap, response, jsonBody);
            this.doWrapMessage(wrap, response, jsonBody);
            this.doWrapValue(wrap, result, resultType, jsonBody);
            this.doWrapStatus(wrap, response, jsonBody);
            this.doWrapTrack(wrap, response, jsonBody);
            doWrap(wrap, result);
            return wrap;
        } finally {
            this.response.remove();
            this.jsonBody.remove();
        }
    }

    protected IResponse getStatus(Object result) {
        IResponse response = IResponse.success();
        if (result instanceof IResponse) {
            response = (IResponse) result;
        } else if (result instanceof Throwable) {
            response = ExceptionStatusResolver.resolveStatus((Throwable) result);
        }
        return response;
    }

    protected void doWrap(Map<String, Object> wrapper, Object result) {

    }

    protected void doWrapCode(Map<String, Object> wrap, IResponse response, JsonBody jsonBody) {
        String name = this.getCodePropertyName();
        if (!Utils.isBlank(name)) {
            String code = response.getCode() == null ? IResponse.SUCCESS_CODE : response.getCode();
            wrap.put(name, code);
        }
    }

    protected void doWrapMessage(Map<String, Object> wrap, IResponse response, JsonBody jsonBody) {
        String name = this.getMessagePropertyName();
        if (!Utils.isBlank(name)) {
            String message = response.getMessage();
            if (Utils.isBlank(message) && response.isSuccess()) {
                message = IResponse.SUCCESS_MESSAGE;
            }
            wrap.put(name, message);
        }
    }

    protected void doWrapValue(Map<String, Object> wrap, Object result, MethodParameter resultType, JsonBody jsonBody) {
        if (result instanceof Throwable) {
            return;
        }

        String name = this.getValuePropertyName();
        if (Utils.isBlank(name)) {
            return;
        }

        Class<?> cls = resultType.getParameterType();
        if (void.class.equals(cls)) {
            return;
        }

        Object value = null;
        if (result instanceof IResponseContext) {
            IResponseContext responseContext = (IResponseContext) result;
            String[] names = responseContext.attributeNames();
            if (null != names && 0 != names.length) {
                Map<String, Object> map = new HashMap<String, Object>();
                for (String property : names) {
                    map.put(property, responseContext.getAttribute(property));
                }
                value = map;
            } else {
                return;
            }
        } else if (result instanceof IResponse) {//如果返回值是一个状态，为了避免和状态重复，就不再设置返回值
            return;
        } else {
            value = result;
        }

        String innerName = null != jsonBody ? jsonBody.value() : null;
        if (!Utils.isBlank(innerName)) {
            Map<String, Object> map = new HashMap<String, Object>();
            map.put(innerName, value);
            wrap.put(name, map);
        } else {
            wrap.put(name, value);
        }
    }

    protected void doWrapStatus(Map<String, Object> wrap, IResponse response, JsonBody jsonBody) {
        String name = this.getStatusPropertyName();
        if (!Utils.isBlank(name)) {
            wrap.put(name, response.isSuccess());
        }
    }

    protected void doWrapTrack(Map<String, Object> wrap, IResponse response, JsonBody jsonBody) {
        String name = this.getTrackIdPropertyName();
        if (!Utils.isBlank(name)) {
            wrap.put(name, response.getTrackId());
        }
    }

    private JsonBody initJsonBody(MethodParameter resultType) {
        JsonBody jsonBody = resultType.getMethodAnnotation(JsonBody.class);
        this.jsonBody.set(jsonBody);
        return jsonBody;
    }

    private IResponse initStatus(Object result) {
        IResponse response = getStatus(result);
        this.response.set(response);
        return response;
    }

    public String getCodePropertyName() {
        JsonBody jb = this.getJsonBody();
        return null != jb ? jb.codePropertyName() : codePropertyName;
    }

    public void setCodePropertyName(String codePropertyName) {
        this.codePropertyName = codePropertyName;
    }

    public String getMessagePropertyName() {
        JsonBody jb = this.getJsonBody();
        return null != jb ? jb.messagePropertyName() : this.messagePropertyName;
    }

    public void setMessagePropertyName(String messagePropertyName) {
        this.messagePropertyName = messagePropertyName;
    }

    public String getValuePropertyName() {
        JsonBody jb = this.getJsonBody();
        return null != jb ? jb.valuePropertyName() : this.valuePropertyName;
    }

    public void setValuePropertyName(String valuePropertyName) {
        this.valuePropertyName = valuePropertyName;
    }

    public String getStatusPropertyName() {
        JsonBody jb = this.getJsonBody();
        return null != jb ? jb.statusPropertyName() : this.statusPropertyName;
    }

    public void setStatusPropertyName(String statusPropertyName) {
        this.statusPropertyName = statusPropertyName;
    }

    public String getValidateErrorsPropertyName() {
        JsonBody jb = this.getJsonBody();
        return null != jb ? jb.validateErrorsPropertyName() : this.validateErrorsPropertyName;
    }

    public void setValidateErrorsPropertyName(String validateErrorsPropertyName) {
        this.validateErrorsPropertyName = validateErrorsPropertyName;
    }

    public String getTrackIdPropertyName() {
        JsonBody jb = this.getJsonBody();
        return null != jb ? jb.trackIdPropertyName() : this.trackIdPropertyName;
    }

    public void setTrackIdPropertyName(String trackIdPropertyName) {
        this.trackIdPropertyName = trackIdPropertyName;
    }
}
